// Fill out your copyright notice in the Description page of Project Settings.


#include "RubikActor.h"

// Sets default values
ARubikActor::ARubikActor(): dimension(2), cubeExtent(100.0f), _cubeActor(nullptr), _isRotating(false)
{
 	// Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
	PrimaryActorTick.bCanEverTick = true;
	ConstructorHelpers::FObjectFinder<UBlueprint> cubeBP(TEXT("Blueprint'/Game/Blueprints/Cube_BP'"));
	if(cubeBP.Succeeded()) {
		cube_BP = (UClass*)cubeBP.Object->GeneratedClass;
	}
}

// Called when the game starts or when spawned
void ARubikActor::BeginPlay()
{
	Super::BeginPlay();
	createRubik();
	_playerController = GetWorld()->GetFirstPlayerController();
	auto inputComp = _playerController->InputComponent;
	if(nullptr != inputComp) {
		inputComp->BindAxis("CameraPitch", this, &ARubikActor::pitchCube);
		inputComp->BindAxis("CameraYaw", this, &ARubikActor::yawCube);
	} else {
		GEngine->AddOnScreenDebugMessage(-1, 6000.0f, FColor::Red, "InputComponent is nullptr");
	}
}

// Called every frame
void ARubikActor::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);

	if (!_isRotating || nullptr == _cubeActor) {
		return;
	}

	FRotator newRotation = _cubeActor->GetActorRotation();
	newRotation.Pitch += _cameraInput.Y;
	newRotation.Yaw += _cameraInput.X;
	_cubeActor->SetActorRotation(newRotation);

	// FVector cameraLocation = _playerController->PlayerCameraManager->GetCameraLocation();
	// FVector cameraDirection = _playerController->PlayerCameraManager->GetCameraRotation().Vector();

	// FVector hitLocation = _hitLocation;
	// FVector hitNormal = FVector::ZeroVector;
	// FHitResult hitResult;
	// FCollisionQueryParams params;
	// params.AddIgnoredActor(_cubeActor);
}

static bool GetBPBoolValueByName(const UObject *target, const FName &propertyName, bool &outValue) {
	const FProperty *property = FindFieldChecked<FProperty>(target->GetClass(), propertyName);
	if(property != nullptr) {
		const FBoolProperty *boolProperty = Cast<FBoolProperty>(property);
		if(boolProperty != nullptr) {
			outValue = boolProperty->GetPropertyValue_InContainer(target);
			return true;
		}
	}
	return false;
}

static bool SetBPBoolValueByName(UObject *target, const FName &propertyName, bool newValue) {
	const FProperty *property = FindFieldChecked<FProperty>(target->GetClass(), propertyName);
	if(property != nullptr) {
		const FBoolProperty *boolProperty = Cast<FBoolProperty>(property);
		if(boolProperty != nullptr) {
			boolProperty->SetPropertyValue_InContainer(target, newValue);
			return true;
		}
	}
	return false;
}

void ARubikActor::BeginRotateCube(AActor *cubeActor, FVector hitLocation) {
	check(cubeActor != nullptr);

	if(_isRotating) {
		return;
	}

	_isRotating = true;
	_cubeActor = cubeActor;
	_hitLocation = hitLocation;
	_cameraLocation = _playerController->PlayerCameraManager->GetCameraLocation();
	_cameraDirection = _playerController->PlayerCameraManager->GetCameraRotation().Vector();

	//GEngine->AddOnScreenDebugMessage(-1, 6000.0f, FColor::Red, _cameraLocation.ToString());
	// GEngine->AddOnScreenDebugMessage(-1, 6000.0f, FColor::Red, FString::Printf("BeginRotateCube = %s", *_cameraLocation.ToString()));

	getMouseOnTouchPlane();
}

void ARubikActor::EndRotateCube() {
	_isRotating = false;
}

void ARubikActor::pitchCube(float axisValue) {
	if(!_isRotating) {
		return;
	}

	_cameraInput.Y = axisValue;
}

void ARubikActor::yawCube(float axisValue) {
	if(!_isRotating) {
		return;
	}

	_cameraInput.X = axisValue;
}

FVector ARubikActor::getMouseOnTouchPlane() {
	FVector worldLocation, worldDirection;
	_playerController->DeprojectMousePositionToWorld(worldLocation, worldDirection);
	//DrawDebugLine(GetWorld(), worldLocation, worldLocation + worldDirection * 1000.0f, FColor::Red, false, 1.0f);
	auto dist = FVector::PointPlaneDist(worldLocation, _hitLocation, _cameraDirection);
	auto cosine = FMath::Abs(FVector::DotProduct(worldDirection, _cameraDirection));
	//GEngine->AddOnScreenDebugMessage(-1, 6000.0f, FColor::Green, worldLocation.ToString());
	//GEngine->AddOnScreenDebugMessage(-1, 6000.0f, FColor::Blue, worldDirection.ToString());
	return worldLocation + worldDirection * (dist / cosine);
}

void ARubikActor::createRubik() {
	check(cube_BP != nullptr);

	float startZ = 0.0f;
	float startXY = (1.0f - dimension) * 0.5f;
	float posZ = startZ, posY, posX;

	for(int z = 0; z < dimension; ++z) {
		posY = startXY;
		TArray<TArray<AActor*>> yArr;
		for(int y = 0; y < dimension; ++y) {
			posX = startXY;
			TArray<AActor*> xArr;
			for(int x = 0; x < dimension; ++x) {
				FVector loc(posX, posY, posZ);
				loc = loc * cubeExtent;
				FActorSpawnParameters params;
				params.Owner = this;
				AActor* cube = GetWorld()->SpawnActor<AActor>(cube_BP, loc, FRotator::ZeroRotator, params);
				cube->SetOwner(this);
				cube->AttachToActor(this, FAttachmentTransformRules::KeepRelativeTransform);
				xArr.Add(cube);
				posX += 1.0f;
			}
			yArr.Add(xArr);
			posY += 1.0f;
		}
		_cubeMatrix.Add(yArr);
		posZ += 1.0f;
	}
}